﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace XFFSM
{
    public class FSMController : MonoBehaviour
    {

        #region 字段

        [SerializeField]
        private RuntimeFSMController _runtimeFSMController;

        private RuntimeFSMController controller;

        // 所有的参数
        internal Dictionary<string, FSMParameterData> parameters = new Dictionary<string, FSMParameterData>();

        // 所有的状态
        internal Dictionary<string, FSMStateNode> states = new Dictionary<string, FSMStateNode>();

        internal List<FSMTransition> transitions = new List<FSMTransition>();

        // 缓存组件
        private Dictionary<string, Dictionary<Type, UnityEngine.Object>> components = new Dictionary<string, Dictionary<Type, UnityEngine.Object>>();

#if UNITY_EDITOR
        [Tooltip("如果一个游戏物体身上有多个FSMController组件,可通过此字段控制FSMEditorWindow中显示哪一个!")]
        public bool ShowInEditorWindow;
#endif

        #endregion

        #region 属性

        public RuntimeFSMController RuntimeFSMController
        {
            get
            {
                if (Application.isPlaying)
                {
                    return controller;
                }
                return _runtimeFSMController;
            }
            set
            {
                _runtimeFSMController = value;
                if (Application.isPlaying)
                    Init();
            }
        }

        internal static IExecuteState executeState { get; private set; } = new DefualtExecuteState();


        // 默认状态
        internal FSMStateNode defaultState { get; private set; } = null;

        // 当前正在执行的状态
        public FSMStateNode currentState { get; private set; } = null;

        // 当前的过渡
        public FSMTransition currentTransition { get; private set; } = null;


        /// <summary>
        /// 当前正在执行的状态逻辑脚本对象
        /// </summary>
        public IFSMState CurrentFSMState => executeState != null ? executeState.GetState(currentState.data.StateName) : null;


        // 存放上一帧当前状态,用来检测状态是否发生改变
        private FSMStateNode lastNode = null;
        #endregion

        #region 生命周期

        private void Awake()
        {
            Init();
        }

        private void Update()
        {
            if (currentState != null) currentState.OnUpdate();

            // 监听
            if (currentState != null && currentState != lastNode)
            {
                lastNode = currentState;
                // 检测当前的状态是否有已经满足条件的过渡
                foreach (var item in transitions)
                {
                    if (item.Data.fromStateName.Equals(currentState.data.name) && item.IsMeet)
                    {
                        SwitchState(item.ToState, item);
                        break;
                    }
                }
            }

        }

        private void LateUpdate()
        {
            if (currentState != null) currentState.OnLateUpdate();
        }

        private void FixedUpdate()
        {
            if (currentState != null) currentState.OnFixedUpdate();
        }

        private void OnDestroy()
        {
            ClearIFSMState();
        }

        #endregion

        #region 方法

        // 初始化
        private void Init()
        {

            // 把数据复制一份
            if (_runtimeFSMController == null) return;
            controller = GameObject.Instantiate(_runtimeFSMController);
            // 参数
            parameters.Clear();
            for (int i = 0; i < controller.parameters.Count; i++)
            {

                if (parameters.ContainsKey(controller.parameters[i].name))
                {
                    Debug.LogErrorFormat("参数名称重复:{0}", controller.parameters[i].name);
                    continue;
                }
                controller.parameters[i].onValueChange = null;
                parameters.Add(controller.parameters[i].name, controller.parameters[i]);
            }

            // 状态
            states.Clear();
            for (int i = 0; i < controller.states.Count; i++)
            {
                FSMStateNodeData stateData = controller.states[i];
                FSMStateNode state = new FSMStateNode(stateData, this);

                if (states.ContainsKey(stateData.name))
                {
                    Debug.LogErrorFormat("状态名称重复:{0}", stateData.name);
                    continue;
                }

                if (stateData.defaultState)
                {
                    defaultState = state;
                }

                states.Add(stateData.name, state);
            }
            // 过渡
            transitions.Clear();
            foreach (var item in controller.transitions)
            {
                FSMTransition transition = new FSMTransition(this, item);
                transitions.Add(transition);
            }

            // 找到默认状态 切换
            SwitchState(defaultState, null);

            // 检测一下 已满足的过渡 判断是否需要切换状态
            foreach (var item in transitions)
            {
                item.CheckConditionIsMeet();
            }

        }

        public void SetBool(string name, bool v)
        {
            SetParamter(name, v ? 1 : 0, ParameterType.Bool);
        }

        public void SetFloat(string name, float v)
        {
            SetParamter(name, v, ParameterType.Float);
        }

        public void SetInt(string name, int v)
        {
            SetParamter(name, v, ParameterType.Int);
        }

        public void SetTrigger(string name) {
            SetParamter(name, 1, ParameterType.Trigger);
        }

        public void ResetTrigger(string name) {
            SetParamter(name, 0, ParameterType.Trigger);
        }

        private void SetParamter(string name, float v, ParameterType type)
        {
            FSMParameterData parameter;
            if (parameters.TryGetValue(name, out parameter))
            {
                if (parameter.parameterType == type)
                    parameter.Value = v;
                else
                    Debug.LogFormat("修改参数失败:{0},类型错误:{1}", name, parameter.parameterType);
            }
            else
                Debug.LogFormat("未查询到参数:{0}", name);
        }

        public static void SetIExecuteState(IExecuteState execute)
        {
            if (execute != null)
            {
                executeState = execute;
            }
        }

        internal void SwitchState(FSMStateNode state, FSMTransition transition)
        {

            if (state == null) return;

            if (currentState != null)
                currentState.OnExit();

            currentState = state;

            state.OnEnter();

            currentTransition = transition;

        }

        public T GetComponent<T>(string path = "") where T : UnityEngine.Object
        {
            return GetComponent(typeof(T), path) as T;
        }

        public UnityEngine.Object GetComponent(Type type, string path = "")
        {

            if (!components.ContainsKey(path))
            {
                components.Add(path, new Dictionary<Type, UnityEngine.Object>());
            }

            if (components.ContainsKey(path) && components[path].ContainsKey(type))
            {
                return components[path][type];
            }

            UnityEngine.Object component = null;

            if (string.IsNullOrEmpty(path))
            {
                component = transform.GetComponent(type);
            }
            else
            {
                Transform child = transform.Find(path);

                if (child != null)
                {
                    component = child.GetComponent(type);
                }
                else
                {
                    Debug.LogErrorFormat("未查询到子节点:{0}", path);
                }

            }

            if (component != null)
            {
                components[path].Add(type, component);
            }

            return component;
        }

        public void ClearIFSMState()
        {

            if (controller == null) return;

            // 清理创建的状态对象
            foreach (var item in controller.states)
            {
                executeState.ClearState(item.StateName);
            }
        }

        #endregion

    }

}

